1. Описание данных
  2. Загрузка данных
  3. Предобработка данных
    • 3.1 Проверка корректности наименований колонок
    • 3.2 Исследование соответствие типов
    • 3.3 Обработка дубликатов
    • 3.4 Изучение пропущенных значений
  4. Анализ данных
    • 4.1 Сегментация покупателей на основе их покупок
    • 4.2 Анализ предпочтений категорий товаров для пользователей каждого сегмента
    • 4.3 Анализ сезонности категорий товаров для пользователей каждого сегмента по годам, месяцам и дням
  5. Проверка гипотез
    • 5.1 Частота покупок покупателей из разных сегментов статистически значимо отличается
    • 5.2 Средний чек покупателей из разных сегментов статистически значимо отличается
  6. Вывод
  7. Материалы
    • 7.1 Презентация

Описание данных¶

Виктория:

1.Название проекта: Проект: E-commerce — Выявление профилей потребления

2.Описание проекта: Являясь аналитиком интернет-магазина товаров для дома «Пока все ещё тут», необходимо расчитать метрики и создать гипотезы на основе полученных данных, чтобы помочь магазину стать лучше.

3.Описание данных: лог сервера с данными о заказах /datasets/ecom_dataset_upd.csv

— date — дата заказа;

— customer_id — идентификатор покупателя;

— order_id — идентификатор заказа;

— product — наименование товара;

— quantity — количество товара в заказе;

— price — цена товара.

Загрузка данных¶

In [1]:
#импортирю библиотеки

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

from plotly import graph_objects as go
import plotly.express as px
In [2]:
url = 'https://code.s3.yandex.net/datasets/'
filename = 'ecom_dataset_upd.csv'

df = pd.read_csv(url + filename)
In [3]:
#вывожу датафрейм

df.head()
Out[3]:
date customer_id order_id product quantity price
0 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Алое Вера, d12, h30 1 142.0
1 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Кофе Арабика, d12,... 1 194.0
2 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Радермахера d-12 см h-20 см 1 112.0
3 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Хризолидокарпус Лутесценс d-9 см 1 179.0
4 2018100100 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Циперус Зумула d-12 см h-25 см 1 112.0

Предобработка данных¶

Проверка корректности наименований колонок¶

In [4]:
#смотрю информацию по датасету

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7474 entries, 0 to 7473
Data columns (total 6 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   date         7474 non-null   int64  
 1   customer_id  7474 non-null   object 
 2   order_id     7474 non-null   int64  
 3   product      7474 non-null   object 
 4   quantity     7474 non-null   int64  
 5   price        7474 non-null   float64
dtypes: float64(1), int64(3), object(2)
memory usage: 350.5+ KB

Виктория: Названия колонок соотвествуют данным, а также изложены чётко и в нижнем регистре

Исследование соответствие типов¶

In [5]:
#смотрю информацию по датасету

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7474 entries, 0 to 7473
Data columns (total 6 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   date         7474 non-null   int64  
 1   customer_id  7474 non-null   object 
 2   order_id     7474 non-null   int64  
 3   product      7474 non-null   object 
 4   quantity     7474 non-null   int64  
 5   price        7474 non-null   float64
dtypes: float64(1), int64(3), object(2)
memory usage: 350.5+ KB

Виктория: По коду видно, что тип данных в колонке 'date' - int64. Это значит, что необходимо привести к верному типу данный столбец

In [6]:
#преобразовываю столбец 'date' в тип данных datetime

df['date'] = pd.to_datetime(df['date'], format='%Y%m%d%H')
In [7]:
#создаю новые столбцы для выделения годов, месяцев и дней из столбца 'date'

df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day

Обработка дубликатов¶

In [8]:
print(f'Дубликатов в датасете: {df.duplicated().sum()}')
Дубликатов в датасете: 0

Виктория: Дубликатов в датасете 0

Изучение пропущенных значений¶

In [9]:
#проверяю пропущенные значения в датасете

df.isna().sum()
Out[9]:
date           0
customer_id    0
order_id       0
product        0
quantity       0
price          0
year           0
month          0
day            0
dtype: int64

Виктория: С пропущенными значениями также повезло - 0 пропущенных значений по всем колонкам

Виктория: С дубликатами здесь все не так однозначно, как это кажется на первый взгляд. Попробую посмотреть, если ли дубликаты без учета дат. Также посмотрю сгруппированные данные по order_id и посчитаю, сколько уникальных пользователей совершили каждый заказ.

Также проверяю даные на выбросы. Если таковые имеются, они могут сильно исказить наши результаты исследования.

In [10]:
#смотрю дубликаты без учёта дат

duplicates = df.duplicated(subset=['customer_id', 'order_id'])
print('Дубликатов без учёта дат:', duplicates.sum())
Дубликатов без учёта дат: 3920

Виктория: Ого! Действительно обнаружилось аж 3920 строк с дубликатами без учёта дат. Устраняем неполадку!

In [11]:
#удаляю дубликаты

df = df.drop_duplicates(subset=['customer_id', 'order_id'])
In [12]:
#группирую данные по order_id и считаю, сколько уникальных пользователей совершили каждый заказ
        
unique_customer = pd.DataFrame(df.groupby('order_id',
                                          as_index=False)['customer_id'] \
                               .nunique() \
                               .sort_values(by='customer_id',
                                            ascending=False)) \
                               .rename(columns={'customer_id':'cnt_unique_users'})

print(f'Всего заказов: {len(unique_customer)}')
print(f'Заказов с 1 уникальным клиентом: {len(unique_customer.query("cnt_unique_users == 1"))}')
Всего заказов: 3521
Заказов с 1 уникальным клиентом: 3492
In [13]:
unique_customer.head(5)
Out[13]:
order_id cnt_unique_users
2610 72845 4
1914 71480 3
902 69485 3
862 69410 2
1799 71226 2

Виктория: В датасете оказались заказы, которые совершили несколько клиентов, что невозможно. Принято решение оставить только уникальные клиенты и заказы

In [14]:
#удаляю строки с дублирующими клиентами на один заказ

df = df.merge(unique_customer, on='order_id', how='left')
df = df.drop_duplicates(subset=['customer_id', 'order_id', 'product', 'cnt_unique_users'], keep='first')

#удаляю ненужный столбец
df = df.drop('cnt_unique_users', axis=1)
In [15]:
df = df.drop_duplicates(subset='order_id')

Виктория: Была проведена работа с дублирующими строками в датасете, а также удалены излишние данные

In [16]:
#анализ количества товаров в заказах

quantity = df.groupby('order_id').agg({'quantity':'sum'}).reset_index()
quantity = quantity.quantity.value_counts()
quantity.head(5)
Out[16]:
quantity
1     2769
2      303
3       89
4       61
10      59
Name: count, dtype: int64
In [17]:
#строю ящик с усами для количества товаров в заказах

fig = px.box(df, y='quantity')
fig.update_layout(title='Общее количество товаров по заказам',
                  yaxis_title='Количество товаров',
                  legend_title='Количество товаров')

fig.show()
In [18]:
#ограничиваю ящик с усами для количества товаров в заказах до 200

fig = px.box(df, y='quantity', color_discrete_sequence=px.colors.qualitative.Prism)
fig.update_layout(title='Общее количество товаров по заказам до 200',
                  yaxis_title='Количество товаров',
                  legend_title='Количество товаров', 
                  yaxis=dict(range=[0,200])
                 )

fig.show()

Виктория: По графику и по коду, можно заметить наличие выбросов в количестве товаров по заказам. Некоторые заказы, такие как заказ на 1000 товаров, выглядят аномальными. Также вызывают подозрение заказы на 300 и 334 товара. В связи с этим, было принято решение оставить только заказы, содержащие до 200 товаров.

Виктория: Смотрю внимательно на топ-5 или 10 по количеству товаров, и по ним принимаю решение.

In [19]:
quantity.head(10)
Out[19]:
quantity
1     2769
2      303
3       89
4       61
10      59
5       42
6       29
15      21
30      16
7       16
Name: count, dtype: int64

Виктория: По топ-10 по количеству товаров можно сделать вывод, что люди покупают больше всего по 1 товару в заказе. Самое большее количество товаров в топ-10 - это 30 товаров в одном заказе.

Анализ данных¶

Сегментация покупателей на основе их покупок¶

Виктория: Ниже провожу сегментацию покупателей на основе их покупок методом RFM

Виктория: Ниже добавила столбец с чеком за заказ и код с учётом чека

In [20]:
#создаю новый столбец order_check в датасете с чеком за заказ

df['order_check'] = round(df['price'] * df['quantity'], 2)
df.head()
Out[20]:
date customer_id order_id product quantity price year month day order_check
0 2018-10-01 00:00:00 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Алое Вера, d12, h30 1 142.0 2018 10 1 142.0
1 2018-10-01 08:00:00 375e0724-f033-4c76-b579-84969cf38ee2 68479 Настенная сушилка для белья Gimi Brio Super 100 1 824.0 2018 10 1 824.0
2 2018-10-01 08:00:00 6644e5b4-9934-4863-9778-aaa125207701 68478 Таз пластмассовый 21,0 л круглый "Водолей" С61... 1 269.0 2018 10 1 269.0
3 2018-10-01 09:00:00 c971fb21-d54c-4134-938f-16b62ee86d3b 68480 Чехол для гладильной доски Colombo Persia Beig... 1 674.0 2018 10 1 674.0
4 2018-10-01 11:00:00 161e1b98-45ba-4b4e-8236-e6e3e70f6f7c 68483 Вешалка для брюк металлическая с резиновым пок... 10 82.0 2018 10 1 820.0
In [21]:
#рассчитываю значения Recency, Frequency и Monetary для каждого клиента

rfm_data = df.groupby('customer_id').agg({
    'date': lambda x: (df['date'].max() - x.max()).days,  # Recency (Последняя активность клиента)
    'order_id': 'count',  # Frequency (Частота)
    'order_check': 'mean'  # Monetary (Средний чек)
}).reset_index()

#разделяю каждый параметр на квартили

rfm_data['r_quartile'] = pd.qcut(rfm_data['date'], q=4, labels=False)
rfm_data['f_quartile'] = pd.qcut(rfm_data['order_id'], q=4, labels=False, duplicates='drop')
rfm_data['m_quartile'] = pd.qcut(rfm_data['order_check'], q=4, labels=False)

Виктория: Из проведенного анализа распределения количества заказов по каждому сегменту пользователей видно, что особенно выделяется категория клиентов из группы 213, которые совершают наибольшее количество заказов. Далее следуют категории 212 и 003. Самое малое количество заказов было сделано группами клиентов 310 и 110. Эти данные могут быть полезны для формирования маркетинговых стратегий и акций, направленных на увеличение числа заказов от клиентов из групп 310 и 110.

In [22]:
#cоздаю новый столбец 'rfm' и присваиваю каждому клиенту комбинацию категорий

rfm_data['rfm_category'] = rfm_data['r_quartile'].astype(int) \
                        + rfm_data['f_quartile'].astype(int) \
                        + rfm_data['m_quartile'].astype(int)


#укрупняю сегменты клиентов

rfm_data['rfm_segment'] = pd.qcut(rfm_data['rfm_category'],
                                  q=4,
                                  labels=['Lost Customer',
                                          'Low-value customer',
                                          'Medium value customer',
                                          'Top Customer'])


# добавляю столбец 'rfm_segment' в исходный DataFrame

df = df.merge(rfm_data[['customer_id', 'rfm_segment']], on='customer_id', how='left')

Виктория: Ниже добавила код и визуализацию

In [23]:
#смотрю, количество заказов по каждой категории клиентов

rfm_segment = df.groupby('rfm_segment') \
                        .agg({'customer_id':'nunique'}) \
                        .reset_index() \
                        .sort_values(by='customer_id',
                                     ascending=False) \
                        .rename(columns={'customer_id':'purchase_frequency'})
rfm_segment
Out[23]:
rfm_segment purchase_frequency
0 Lost Customer 952
1 Low-value customer 597
2 Medium value customer 441
3 Top Customer 440
In [24]:
#визуализирую распределение категории клиентов по количеству заказов в виде столбчатой диаграммы

fig = px.bar(rfm_segment,
             x='rfm_segment',
             y='purchase_frequency',
             text='purchase_frequency',
             color_discrete_sequence=px.colors.qualitative.Pastel)

fig.update_layout(title='Распределение категории клиентов по количеству заказов', 
                 xaxis_title='Категория клиента', 
                 yaxis_title='Количество заказов')

fig.show()

Виктория: ВЫВОД

Исходя из анализа данных о сегментах клиентов, можно сделать следующие выводы:

  1. Наибольшую группу клиентов составляют "Потерянные клиенты" (Lost Customer) - 952 клиента. Это может указывать на то, что эти клиенты не совершали покупок в течение определенного периода времени и требуют дополнительных усилий для их возвращения и удержания.

  2. Следующий по величине сегмент клиентов - "Клиенты с низкой стоимостью" (Low-value customer) - 597. Это означает, что эти клиенты делают относительно небольшое количество покупок и приносят меньший доход компании. Важно разработать стратегию для стимулирования их активности и увеличения стоимости покупок.

  3. Сегмент "Клиенты средней стоимости" (Medium value customer) составляют третью по величине группу клиентов с показателем 440. Они делают меньше покупок и приносят меньший доход, чем "Топ-клиенты", но больше, чем "Клиенты с низкой стоимостью". Рекомендуется разработать стратегию для стимулирования их активности и увеличения стоимости покупок.

  4. "Топ-клиенты" (Top Customer) являются наименьшей группой - 438 пользователя. Эти клиенты являются самыми активными и приносят наибольший доход компании. Стратегия должна быть направлена на удержание и дальнейшее развитие отношений с этой группой клиентов.

Анализ предпочтений категорий товаров для пользователей каждого сегмента¶

In [25]:
#добавляю новый столбец 'product_name'

df['product_name'] = df['product'].str.split().str[:2].str.join(' ')
df['product_name'] = df['product_name'].str.lower()
In [26]:
#ИСПРАВЛЕННЫЙ КОД

#расформировываю категорию "Другое" по ключевым словам

df.loc[df['product_name'].str.contains('котел|светильник|измерительный|фоторамка|этажерка|электроштопор|простынь|фиттония|орехоколка|электрический|миксер|радиаторный|картофелемялка|утюг|пылесос|окномойка|чайник|термокружка|весы|tepмокружка|измельчитель'), 'product_name_category'] = 'Техника и аксессуары'
df.loc[df['product_name'].str.contains('кодонанта|суккулент|кампанула|иссоп|аспарагус|карниз|артемизия|антижир|мантоварка|бархатцы|алиссум|подсолнечник|пуансетия|гиностемма|ель|защитный экран|альбука спиралис|розмарин d-9|гимнокалициум микс|папоротник|осина|лавр|эпипремнум|флокс|аквилегия|душица|муррайя|джункус|драцена|нефролепис'), 'product_name_category'] = 'Растения'
df.loc[df['product_name'].str.contains('шприц|лоток|сахарница|скалка|пакет|тортница|скребок|венчик|сито|рыбочистка|ковш|противень|блюдце|блюдо|крышка|хлебница|кастрюля|масленка|салфетка|набор|тарелка|ложка|нож|вилка|сковорода|салатник|сотейник|скатерть|кувшин|банка|контейнер|бидон|кружка|термос|овощеварка|миска|овощечистка|бульонница|толкушка|соковарка|форма'), 'product_name_category'] = 'Посуда'
df.loc[df['product_name'].str.contains('обувница|ключница|термометр|веник|коробка|урна|комод|детский|для|коврик|крючок|корзина|доска|полка|полки|ящик|кашпо|ковер|стяжка|сиденье|терка|подголовник bacchetta|складная картонная|модульная стеклянная|подушка декоративная|корзинка|уголок|ковёр придверный'), 'product_name_category'] = 'Мебель'
df.loc[df['product_name'].str.contains('маска|махровый|гладильная|ведро|ванна|петля|вантуз|паста|мыльница|таз|сушилка|щетка|корыто|полотенце|швабра|перчатки|тряпкодержатель'), 'product_name_category'] = 'Ванная'
df.loc[df['product_name'].str.contains('овсянница|травы|розмарин|томат|кумкват|укроп|лук|виноград|капуста|кориандр|дыня|клубника|клен|чабер|рассада|помидор|базилик|петрушка|тимьян'), 'product_name_category'] = 'Растения'
df.loc[df['product_name'].str.contains('тележка|сумка'), 'product_name_category'] = 'Сумки и тележки'
df.loc[df['product_name'].str.contains('ткань|подушка|наматрасник|наволочка|наматрацник|пододеяльник|покрывало|одеяло|комплект|плед|штора|чехол|халат|вешалки|комплект|плечики|вешалка|подкладка|подрукавник|плед|простыня|штора|покрывало|одеяло|плед'), 'product_name_category'] = 'Спальня'
df.loc[df['product_name'].str.contains('шеффлера|литопс|пахира|молодило|диффенбахия|травянистая|хризолидокарпус|крассула|алоэ|аптения|эхеверия|фатсия|вербейник|комнатное|фал|кофе|хлорофитум|мята|цветок|дендробиум|кипарисовик|растение|дерево|эвкалипт|эхинокактус|циперус|арбуз|табак|соланум|сциндапсус|каланхое|замиокулькас|нолина|кореопсис|хамедорея|зверобой'), 'product_name_category'] = 'Растения'
df.loc[df['product_name'].str.contains('земляника|огурец|календула|гортензия|пеперомия|виола|крокусы|гиацинт|метельчатый|метельчатая|хризантема|ранункулус|калла|фуксия|спатифиллум|гайлардия|гипсофила|котовник|калатея|космея|амариллис|гербера|адиантум|незабудка|примула|лантана|мимоза|вербена|георгина|целозия|физостегия|пиретрум|цинния|змееголовник|цинния|энотера|львиный|тюльпан|колокольчик|импатиенс|скиммия|мускари|синнингия|гардения|гвоздика|вероника|пеларгония|петуния|калибрахоа|роза|тагетис|герань|бакопа|азалия|афеляндра|антуриум|бальзамин|фиалка|\пуансеттия|бегония|декабрист|пуансеттия|настурция|мединилла|цикламен|лаванда'), 'product_name_category'] = 'Цветы'
df.loc[df['product_name'].str.contains('камнеломка|фикус|муляж|искусственная|искусственный|композиция'), 'product_name_category'] = 'Оформление дома'
df.loc[df['product_name'].str.contains('пьезозажигалка|сверло|холодная сварка|многофункциональный инструмент|шило|совок|толкушка|соковарка|половник|ёрш|мерный стакан|линейка, длина|шпингалет|кисточка|решетка|рассекатель|шнур|хром|ручка|петля-стрела|веревка|сметка|штангенциркуль|мирт|шпагат|стремянка|насадка|завертка|стремянки'), 'product_name_category'] = 'Инструменты'
df.loc[df['product_name'].str.contains('защитная соль|нетканые салфетки|салфетница металлическая|гипоаллергенный|отбеливатель|средство|мыло'), 'product_name_category'] = 'Бытовая химия'
In [27]:
#смотрю, на какие категории товаров поделились данные из датасета

product_name_category = df.groupby('product_name_category') \
                        .agg({'order_id':'count'}) \
                        .reset_index() \
                        .sort_values(by='order_id',
                                     ascending=False) \
                        .rename(columns={'order_id':'cnt'})
product_name_category
Out[27]:
product_name_category cnt
10 Цветы 624
1 Ванная 589
6 Растения 428
3 Мебель 422
8 Сумки и тележки 374
5 Посуда 336
7 Спальня 295
4 Оформление дома 234
2 Инструменты 135
9 Техника и аксессуары 61
0 Бытовая химия 23
In [28]:
#визуализирую распределение категории прдуктов по количеству заказов в виде столбчатой диаграммы

fig = px.bar(product_name_category, x='product_name_category',
             y='cnt',
             text='cnt',
             color_discrete_sequence=px.colors.qualitative.Prism)

fig.update_layout(title='Распределение категории продуктов по количеству заказов', 
                 xaxis_title='Категория товара', 
                 yaxis_title='Количество заказов')

fig.show()

Виктория: Исходя из анализа данных о распределении категории продуктов по количеству заказов, можно сделать следующие выводы:

  1. Самой популярной товарной группой являются "Цветы" с общим количеством заказов равным 624. Это указывает на высокий спрос на цветочные товары и их значимость для клиентов.

  2. "Ванная" занимает второе место по популярности с количеством заказов равным 588. Это говорит о том, что товары для ванной комнаты также являются востребованными среди клиентов.

  3. "Растения" и "Мебель" занимают третье и четвертое места соответственно с количеством заказов 428 и 422. Эти товарные группы также пользуются значительным спросом среди клиентов.

  4. "Сумки и тележки" и "Посуда" занимают пятую и шестую позиции с количеством заказов 374 и 336 соответственно.

  5. "Спальня", "Оформление дома", "Инструменты", "Техника и аксессуары" и "Бытовая химия" занимают меньшие позиции по популярности с количеством заказов от 294 до 23. Это может указывать на более низкий спрос на эти товарные группы или на их специфичность.

In [29]:
#перевожу тип данных столбца rfm_segment из category в object

df['rfm_segment'] = df['rfm_segment'].astype('object')

#строю таблицу для категорий товаров и категорий клиентов

grouped_by_categories = df.groupby(['rfm_segment', 'product_name_category'],
                                   as_index=False).agg({'order_id':'count'}).rename(columns={'order_id':'cnt'})
grouped_by_categories.head()
Out[29]:
rfm_segment product_name_category cnt
0 Lost Customer Бытовая химия 11
1 Lost Customer Ванная 194
2 Lost Customer Инструменты 42
3 Lost Customer Мебель 172
4 Lost Customer Оформление дома 100
In [30]:
#визуализирую количество заказов по категориям клиентов и товаров

fig = px.bar(grouped_by_categories.sort_values(by='cnt', ascending=False),
             x='cnt',
             y='rfm_segment',
             color='product_name_category',
             orientation='h',
             color_discrete_sequence=px.colors.qualitative.Prism,
             title='Количество заказов по категориям клиентов и товаров')
             
fig.update_layout(xaxis_title='Количество заказов',
                  yaxis_title='Категории клиентов',
                  legend_title='Категории товаров')
                  
fig.show()

Виктория: Исходя из проведенного анализа данных о распределении предпочтений категорий продуктов по категориям клиентов, можно сделать следующие выводы:

  1. У "Потерянных клиентов" (Lost Customer) и "Клиентов с низкой стоимостью" (Low-value customer) наиболее популярной категорией товаров являются цветы. Это может указывать на то, что эти клиенты склонны приобретать товары для украшения и оформления своего пространства.

  2. У "Топ-клиентов" (Top Customer) и "Клиентов средней стоимости" (Medium Value Customer) на первом месте предпочтений находятся предметы для ванной комнаты. Это может указывать на то, что эти клиенты больше интересуются товарами, связанными с комфортом и уходом за собой.

  3. На вторых и третьих позициях продуктов у "Потерянных клиентов" (Lost Customer) идут растения и товары для ванной комнаты, у "Клиентов с низкой стоимостью" (Low-value customer) - товары для ванной комнаты и мебель, у "Топ-клиентов" (Top Customer) - сумки и тележки, а также мебель на третьем месте. У "Клиентов средней стоимости" (Medium Value Customer) на втором месте находятся цветы, а на третьем - мебель.

Таким образом, можно сделать вывод, что предпочтения клиентов в различных категориях продуктов могут быть связаны с их статусом, финансами, потребностями и интересами.

Анализ сезонности категорий товаров для пользователей каждого сегмента по годам, месяцам и дням¶

In [31]:
#строю гистограмму по годам
df['year'].value_counts().plot(kind='bar', grid=True, color='Lavender')

#настраиваю оси и заголовки
plt.xlabel('Год')
plt.ylabel('Количество заказов')
plt.title('Гистограмма количества заказов по годам')
plt.xticks(rotation=45)

#отображаю гистограмму
plt.show()

Виктория: При анализе количества заказов по годам обнаружилось, что заказов с каждым годом становится всё меньше, а с 2018 года идёт спад по продажам.

In [32]:
#добавлюя столбец с сезоном для дальнейшего анализа

df['season'] = df['month'].apply(lambda x: 'winter' if x in [12, 1, 2] \
                                 else 'spring' if x in [3, 4, 5] \
                                 else 'summer' if x in [6, 7, 8] \
                                 else 'autumn')
In [33]:
#добавлюя таблицу с сезонами, категориями клиентов и товаров для дальнейшего анализа

grouped_data = df.groupby(['rfm_segment', 'season', 'product_name_category']).size().reset_index(name='count')
grouped_data = grouped_data.sort_values('season', key=lambda x: x.map({'winter': 1, 'spring': 2, 'summer': 3, 'autumn': 4}))
grouped_data
Out[33]:
rfm_segment season product_name_category count
82 Low-value customer winter Сумки и тележки 30
84 Low-value customer winter Цветы 53
83 Low-value customer winter Техника и аксессуары 4
164 Top Customer winter Техника и аксессуары 7
81 Low-value customer winter Спальня 18
... ... ... ... ...
134 Top Customer autumn Сумки и тележки 39
135 Top Customer autumn Техника и аксессуары 8
51 Low-value customer autumn Сумки и тележки 40
49 Low-value customer autumn Растения 30
0 Lost Customer autumn Бытовая химия 4

166 rows × 4 columns

In [34]:
#строю график для сегмента lost_customer

lost_customer = grouped_data[grouped_data['rfm_segment']=='Lost Customer']

fig = px.line(lost_customer,
              x='season',
              y='count',
              color='product_name_category',
              title='Анализ по сезонам категорий товаров для пользователей сегмента "Lost Customer"')
             
fig.update_layout(xaxis_title='Сезон',
                  yaxis_title='Количество заказов',
                  legend_title='Категории товаров')

fig.show()

Виктория: Исходя из проведенного анализа данных о сезонности категорий товаров по сегменту "Потерянных клиентов" (Lost Customer), можно сделать следующие выводы:

  1. Сегмент "Потерянных клиентов" проявляет наибольший интерес по количеству заказов в категориях "Цветы", "Растения" и товаров для ванной. Это является потенциальной возможностью для повышения спроса на эти товары.

  2. В категории цветы наблюдается сезонность. Интерес к этой категории возрастает весной, вероятно, в связи с праздниками. Затем к лету происходит спад, но в осенние месяцы интерес снова возрастает, возможно, из-за предстоящей школы, дня знаний и дня учителя.

  3. Интерес к растениям увеличивается с лета до осени, но зимой и весной он падает. Это может быть связано с изменением климата и сезонными особенностями ухода за растениями.

  4. Категории товаров для ванны, спальни, мебели и посуды также проявляют сезонность, но в обратном направлении. Интерес к ним падает весной. Возможно, это связано с периодом уборки и обновления интерьера после зимы.

  5. В категории бытовой химии не наблюдается явной сезонности. Это говорит о том, что бытовая химия является неотъемлемой частью повседневной жизни людей и покупается ими в течение всего года. Однако, спрос на нее может быть невысоким, что открывает возможности для увеличения спроса на этот сегмент.

  6. Оформление дома и товары для спальни испытывают снижение интереса летом. Вероятно, это связано с тем, что в это время люди предпочитают проводить время на даче или отдыхать на открытом воздухе.

In [35]:
#строю график для сегмента low_value_customer

low_value_customer = grouped_data[grouped_data['rfm_segment']=='Low-value customer']

fig = px.line(low_value_customer,
              x='season',
              y='count',
              color='product_name_category',
              title='Анализ по сезонам категорий товаров для пользователей сегмента "Low-value Customer"')
             
fig.update_layout(xaxis_title='Сезон',
                  yaxis_title='Количество заказов',
                  legend_title='Категории товаров')

fig.show()

Виктория: Исходя из проведенного анализа данных о сезонности категорий товаров по сегменту "Клиентов с низкой стоимостью" (Low-value customer), можно сделать следующие выводы:

  1. Цветы, растения и товары для ванной являются наиболее популярными категориями среди "Клиентов с низкой стоимостью" (Low-value customer). Эти товары имеют наибольшее количество заказов по сравнению с другими категориями.

  2. Растет интерес к растениям и оформлению дома к весне, что может быть связано с началом сезона садоводства и желанием клиентов обновить интерьер своего дома.

  3. К лету спрос на инструменты, растения и цветы снижается. Возможно, это связано с тем, что в это время "Клиентов с низкой стоимостью" (Low-value customer) стоимостью предпочитают не тратить деньги на товары с высокой стоимостью.

  4. Продажи товаров для ванной падают весной, но затем начинают расти в летние и осенние месяцы. Вероятно, это связано с сезонными изменениями в потребностях клиентов.

  5. Техника и аксессуары не имеют явно выраженной сезонности и покупаются "Клиентов с низкой стоимостью" (Low-value customer) на протяжении всего года. Аналогично, бытовая химия также пользуется стабильным спросом.

In [36]:
grouped_data = grouped_data.sort_values('season', key=lambda x: x.map({'winter': 1, 'spring': 2, 'summer': 3, 'autumn': 4}))

#строю график для сегмента medium_value_customer

medium_value_customer = grouped_data[grouped_data['rfm_segment']=='Medium value customer']

fig = px.line(medium_value_customer,
              x='season',
              y='count',
              color='product_name_category',
              title='Анализ по сезонам категорий товаров для пользователей сегмента "Medium value Customer"')
             
fig.update_layout(xaxis_title='Сезон',
                  yaxis_title='Количество заказов',
                  legend_title='Категории товаров')

fig.show()

Виктория: Исходя из проведенного анализа данных о сезонности категорий товаров по сегменту "Клиентов средней стоимости" (Medium Value Customer), можно сделать следующие выводы:

  1. Клиенты средней стоимости проявляют интерес и рост спроса на товары категории "Сумки и тележки". Это свидетельствует о том, что эти клиенты ценят комфорт и удобство при покупках. Пик спроса на сумки и тележки приходится на весну.

  2. В течение всего года наблюдается стабильный спрос на товары для ванной. Это говорит о том, что клиенты средней стоимости регулярно приобретают такие товары и не зависят от сезонных изменений.

  3. Не выявлено сезонности в спросе на бытовую химию. Это может означать, что клиенты средней стоимости покупают такие товары постоянно, независимо от времени года.

  4. Категории "Цветы" и "Посуда" имеют пик спроса весной, а спад летом. Это может быть связано с тем, что клиенты средней стоимости активнее покупают цветы и посуду в период весенних праздников и сезона отпусков.

  5. Категория "Мебель" имеет резкий спад спроса весной, но затем наблюдается резкий рост от весны до осени. Это может указывать на то, что клиенты средней стоимости предпочитают покупать мебель в более теплые месяцы года.

  6. Категории "Инструменты" набирают спрос от весны до осени.

In [37]:
#строю график для сегмента top_customer

top_customer = grouped_data[grouped_data['rfm_segment']=='Top Customer']

fig = px.line(top_customer,
              x='season',
              y='count',
              color='product_name_category',
              title='Анализ по сезонам категорий товаров для пользователей сегмента "Top Customer"')
             
fig.update_layout(xaxis_title='Сезон',
                  yaxis_title='Количество заказов',
                  legend_title='Категории товаров')

fig.show()

Виктория: Исходя из проведенного анализа данных о сезонности категорий товаров по сегменту "Топ-клиентов" (Top Customer), можно сделать следующие выводы:

  1. Все категории товаров проявляют сезонность от лета до осени. Вероятно, это связано с тем, что летом у клиентов появляется больше времени на приобретение товаров для ванны, сумок и тележек, спальни, посуды, мебели и других категорий. Однако, интерес к этим товарам падает от зимы до весны и далее до лета.

  2. Отдельно стоит отметить категорию инструменты, где наблюдается интересный пик весной.

  3. Категории товаров, связанные с оформлением дома, техникой, растениями и цветами также имеют схожую тенденцию: спад с зимы до лета и рост от лета до осени.

Проверка гипотез¶

Проверка гипотезы_1:¶

  • H0 - Нет различий в частоте покупок у покупателей из разных сегментов
  • H1 - Есть различия в частоте покупок у покупателей из разных сегментов

Виктория:

Для проверки гипотезы о равенстве средних частот покупок и среднего чека между различными сегментами клиентов я подкорректировала тест и использовала тест Манна-Уитни для независимых выборок (если p-значение теста Шапиро-Уилка больше alpha, применяла непараметрический тест Манна-Уитни, в обратном случае t-тест). Этот тест является непараметрическим и не требует предположений о нормальности распределения данных. Однако перед применением теста я сначала всё-таки проверила нормальность распределения частот покупок в каждом сегменте и среднего чека с помощью теста Шапиро-Уилка.

In [38]:
df = df.merge(rfm_segment, on='rfm_segment', how='left')
df.head(2)
Out[38]:
date customer_id order_id product quantity price year month day order_check rfm_segment product_name product_name_category season purchase_frequency
0 2018-10-01 00:00:00 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Алое Вера, d12, h30 1 142.0 2018 10 1 142.0 Low-value customer комнатное растение Растения autumn 597
1 2018-10-01 08:00:00 375e0724-f033-4c76-b579-84969cf38ee2 68479 Настенная сушилка для белья Gimi Brio Super 100 1 824.0 2018 10 1 824.0 Top Customer настенная сушилка Ванная autumn 440
In [40]:
pip install scipy
Requirement already satisfied: scipy in c:\users\79261\appdata\local\programs\python\python311\lib\site-packages (1.12.0)Note: you may need to restart the kernel to use updated packages.
[notice] A new release of pip is available: 23.2.1 -> 24.0
[notice] To update, run: python.exe -m pip install --upgrade pip
Requirement already satisfied: numpy<1.29.0,>=1.22.4 in c:\users\79261\appdata\local\programs\python\python311\lib\site-packages (from scipy) (1.25.2)
In [41]:
import scipy.stats as st 
In [42]:
#задаю уровень значимости alpha
alpha = 0.05

#создаю список rfm_segments, содержащий все возможные значения столбца 'rfm' в датасете
rfm_segments = ['Medium value customer', 'Top Customer', 'Lost Customer', 'Low-value customer']

#создаю пустой список p_values, в который буду добавлять значения p-значений
p_values = []

#для каждого сегмента из списка rfm_segments выполняю следующие действия:
for segment in rfm_segments:
    segment_frequency = df[df['rfm_segment'] == segment]['purchase_frequency'] 
    #выделяю данные о частоте покупок для текущего сегмента
    other_frequency = df[df['rfm_segment'] != segment]['purchase_frequency'] 
    #выделяю данные о частоте покупок для всех остальных сегментов
    
    #проверяю нормальность распределения с помощью теста Шапиро-Уилка
    shapiro_test = st.shapiro(segment_frequency)
    
    #если p-значение теста Шапиро-Уилка больше alpha, применяю непараметрический тест Манна-Уитни
    if shapiro_test.pvalue > alpha:
        results = st.mannwhitneyu(segment_frequency, other_frequency) #применяю тест Манна-Уитни для независимых выборок
    else:
        results = st.ttest_ind(segment_frequency, other_frequency) #применяю t-тест для независимых выборок
    
    p_value = results.pvalue #извлекаю значение p-значения из результатов теста
    
    p_values.append(p_value) #добавляю p_value в список p_values

#устанавливаю уровень статистической значимости исходя из множественной проверки гипотез
alpha_adjusted = alpha / len(rfm_segments)

#для каждого сегмента и его соответствующего p-значения из списка rfm_segments и p_values выполняю следующие действия:
#если p-значение меньше уровня статистической значимости, выводится сообщение о том, что нулевая гипотеза отвергается
#в противном случае выводится сообщение о том, что нет оснований отвергать нулевую гипотезу

for i, segment in enumerate(rfm_segments):
    if p_values[i] < alpha_adjusted:
        print(f'Отвергаем нулевую гипотезу для сегмента {segment}')
    else:
        print(f'Нет оснований отвергать нулевую гипотезу для сегмента {segment}')
Отвергаем нулевую гипотезу для сегмента Medium value customer
Отвергаем нулевую гипотезу для сегмента Top Customer
Отвергаем нулевую гипотезу для сегмента Lost Customer
Отвергаем нулевую гипотезу для сегмента Low-value customer
C:\Users\79261\AppData\Local\Temp\ipykernel_13584\3952553517.py:18: UserWarning:

scipy.stats.shapiro: Input data has range zero. The results may not be accurate.

Виктория: Вывод: отвергаем нулевую гипотезу для всех клиентов. Исходя из результатов анализа, можно сделать вывод, что есть статистически значимые различия в частоте покупок у покупателей из разных сегментов. Это означает, что сегменты покупателей имеют различную активность в покупках.

Проверка гипотезы_2:¶

  • H0: Нет различий в среднем чеке между покупателями из разных сегментов
  • H1: Есть различия в среднем чеке между покупателями из разных сегментов

In [43]:
#смотрю, какой средний чек по заказам делала каждая категория клиентов

avg_check = df.groupby('rfm_segment') \
                .agg({'price':'mean'}) \
                .round(2) \
                .reset_index() \
                .rename(columns={'price':'avg_check'}) \
                .sort_values(by='avg_check',
                             ascending=False)

avg_check
Out[43]:
rfm_segment avg_check
3 Top Customer 1196.74
2 Medium value customer 1043.38
1 Low-value customer 949.11
0 Lost Customer 332.90
In [44]:
df = df.merge(avg_check, on='rfm_segment', how='left')
df.head(2)
Out[44]:
date customer_id order_id product quantity price year month day order_check rfm_segment product_name product_name_category season purchase_frequency avg_check
0 2018-10-01 00:00:00 ee47d746-6d2f-4d3c-9622-c31412542920 68477 Комнатное растение в горшке Алое Вера, d12, h30 1 142.0 2018 10 1 142.0 Low-value customer комнатное растение Растения autumn 597 949.11
1 2018-10-01 08:00:00 375e0724-f033-4c76-b579-84969cf38ee2 68479 Настенная сушилка для белья Gimi Brio Super 100 1 824.0 2018 10 1 824.0 Top Customer настенная сушилка Ванная autumn 440 1196.74
In [45]:
#задаю уровень значимости alpha
alpha = 0.05

#создаю список rfm_segments, содержащий все возможные значения столбца 'rfm' в датасете
rfm_segments = ['Medium value customer', 'Top Customer', 'Lost Customer', 'Low-value customer']

#создаю пустой список p_values, в который буду добавлять значения p-значений
p_values = []

#для каждого сегмента из списка rfm_segments выполняю следующие действия:
for segment in rfm_segments:
    segment_frequency = df[df['rfm_segment'] == segment]['avg_check'] 
    #выделяю данные о среднем чеке для текущего сегмента
    other_frequency = df[df['rfm_segment'] != segment]['avg_check'] 
    #выделяю данные о среднем чеке для всех остальных сегментов
    
    #проверяю нормальность распределения с помощью теста Шапиро-Уилка
    shapiro_test = st.shapiro(segment_frequency)
    
    #если p-значение теста Шапиро-Уилка больше alpha, применяю непараметрический тест Манна-Уитни
    if shapiro_test.pvalue > alpha:
        results = st.mannwhitneyu(segment_frequency, other_frequency) #применяю тест Манна-Уитни для независимых выборок
    else:
        results = st.ttest_ind(segment_frequency, other_frequency) #применяю t-тест для независимых выборок
    
    p_value = results.pvalue #извлекаю значение p-значения из результатов теста
    
    p_values.append(p_value) #добавляю p_value в список p_values

#устанавливаю уровень статистической значимости исходя из множественной проверки гипотез
alpha_adjusted = alpha / len(rfm_segments)

#для каждого сегмента и его соответствующего p-значения из списка rfm_segments и p_values выполняю следующие действия:
#если p-значение меньше уровня статистической значимости, выводится сообщение о том, что нулевая гипотеза отвергается
#в противном случае выводится сообщение о том, что нет оснований отвергать нулевую гипотезу

for i, segment in enumerate(rfm_segments):
    if p_values[i] < alpha_adjusted:
        print(f'Отвергаем нулевую гипотезу для сегмента {segment}')
    else:
        print(f'Нет оснований отвергать нулевую гипотезу для сегмента {segment}')
Отвергаем нулевую гипотезу для сегмента Medium value customer
Отвергаем нулевую гипотезу для сегмента Top Customer
Отвергаем нулевую гипотезу для сегмента Lost Customer
Отвергаем нулевую гипотезу для сегмента Low-value customer
C:\Users\79261\AppData\Local\Temp\ipykernel_13584\189160445.py:18: UserWarning:

scipy.stats.shapiro: Input data has range zero. The results may not be accurate.

Виктория: Вывод: отвергаем нулевую гипотезу для всех клиентов. В результате проведенного анализа можно сделать вывод о наличии различий в среднем чеке между покупателями из разных сегментов. Нулевая гипотеза об отсутствии различий была отвергнута для всех четырех сегментов: Medium value customer, Top Customer, Lost Customer и Low-value customer. Это означает, что средний чек в этих сегментах значительно различается.

Вывод¶

Виктория: Исходя из полученных данных, можно сделать следующие рекомендации для каждого сегмента клиентов:

  • "Потерянные клиенты" (Lost Customer):

1.Описание: Из анализа выявлено, что наибольшую группу клиентов составляют "Потерянные клиенты" (Lost Customer), у котоорых наблюдается низкая средняя сумма покупки. Это может указывать на то, что данные сегменты клиентов склонны приобретать товары с низкой ценой. Анализируя данные о частоте покупок данной категории клиентов стоит отметить, что большинство клиентов (952 чел.) относятся к категории "Потерянные клиенты". Это означает, что магазин очень часто приходят клиенты, но долго не задерживаются и по каким-то причинам перестают покупать, так что надо принять меры для их возвращения и удержания.

2.Предпочтительные категории: Для этого сегмента клиентов наиболее популярными категориями товаров являются цветы. Это указывает на то, что эти клиенты склонны приобретать товары для украшения и оформления своего пространства. Кроме того, у "Потерянных клиентов" (Lost Customer) также популярны растения и товары для ванной комнаты. Для сегмента "Потерянных клиентов" (Lost Customer) наблюдается резкий рост спроса на эти категории товаров в начале 2019 года, однако после этого спрос снижается. Также стоит обратить внимание на категории товаров "Оформление дома" и мебель, которые вызывают интерес у "Потерянных клиентов" (Lost Customer), хотя в меньшей степени.

3.Рекомендация: Необходимо рассмотреть возможность увеличения рекламных предложений в категориях "Цветы", "Растения" и товары для ванной. Эти категории товаров пользуются наибольшим интересом у этого сегмента клиентов. Регулярная рассылка акций и скидок на эти товары может привлечь внимание и повысить спрос. Также стоит учесть сезонность в категории цветы. Нужно увеличить рекламные предложения весной, в преддверии праздников, а также осенью, связанной с началом учебного года. Это поможет увеличить продажи в эти периоды. Повышенный интерес к растениям летом и осенью может быть использован для проведения акций и специальных предложений, чтобы стимулировать покупку.Можно обратить внимание на категорию бытовой химии. Возможно, спрос на этот сегмент не высок, поэтому рекомендуется проводить акции и предложения, чтобы привлечь внимание к этой категории и увеличить продажи. Сезонность в категории оформления дома и товаров для спальни: в летние месяцы, когда интерес к этим товарам снижается, можно сконцентрироваться на других категориях или предложить специальные акции, связанные с отпуском и отдыхом на открытом воздухе.

  • "Клиенты с низкой стоимостью" (Low-value customer):

1.Описание: Второй по величине сегмент клиентов - сегмент "Клиенты с низкой стоимостью" (Low-value customer, 597 клиентов). Из предоставленных данных видно, что средний чек клиента зависит от его сегмента. Низкое значение среднего чека у клиентов с низкой ценностью (877.16), что свидетельствует о низкой чеке покупок клиентов. Однако данные клиенты часто покупают и являются вторым сегментом по частоте покупок, что означает, что их можно стимулировать покупать ещё чаще.

2.Предпочтительные категории: Наиболее популярной категорией товаров в этом сегменте являются цветы, указывая на склонность клиентов к приобретению товаров для украшения и оформления своего пространства. Товары для ванной комнаты и растения также занимают вторые и третьи позиции продуктов в этом сегменте.

3.Рекомендация: предлагается следующая стратегия для сегмента клиентов с низкой стоимостью: рассылать рекламные предложения о цветах, растениях и товарах для ванной на протяжении всего года, так как эти категории товаров наиболее популярны среди данного сегмента клиентов. Особое внимание следует уделить летним и осенним месяцам, когда спрос на эти товары повышается. Весной, когда спрос на инструменты, растения и цветы снижается, можно сосредоточиться на других категориях товаров, которые пользуются стабильным спросом, например, на технику и аксессуары или бытовую химию. Можно проводить специальные акции и предложения в периоды повышенного спроса на растения и оформление дома, например, весной и осенью. Это может стимулировать клиентов с низкой стоимостью к увеличению количества приобретаемых товаров.

  • "Клиенты средней стоимости" (Medium Value Customer):

1.Описание: Сегмент "Клиенты средней стоимости" занимают третью по величине группу клиентов с показателем покупательной активности, равным 440 клиента, но играет не менее важную роль и является перспективным сегментом покупателей, на который стоит обратить внимание. Клиенты со средним значением имеют средний чек в размере 1054.15, что может указывать на их умеренную активность и лояльность.

2.Предпочтительные категории: Предметы для ванной комнаты занимают первое место в предпочтениях этого сегмента, что свидетельствует о большом интересе клиентов к товарам, связанным с комфортом и уходом за собой. Первые три позиции продуктов в сегменте "Клиенты средней стоимости" занимают цветы и сумки и тележки.

3.Рекомендация: Рекомендуется акцентировать внимание на категории "Сумки и тележки" весной, так как именно в этот период проявляется наибольший интерес к товарам этой категории. Стоит предлагать специальные предложения, скидки или бонусы при покупке сумок и тележек весной. Исходя из стабильного спроса на товары для ванной в течение всего года, рекомендуется проводить регулярные рекламные кампании для этой категории товаров. Можно подчеркнуть их универсальность и пользу в повседневной жизни. В связи с отсутствием сезонности в спросе на бытовую химию, стоит предлагать продукты этой категории в рекламных предложениях также круглогодично. Можно, например, вводить программы лояльности или скидки при покупке определенного объема товаров. Цветы и посуда: усилия на рекламе весной, особенно в период весенних праздников. Инструменты (весна-осень): с учетом набора спроса на инструменты от весны до осени, рекомендуется акцентировать внимание на этой категории товаров в этот период. А, учитывая резкий спад спроса на мебель весной, но рост летом и осенью, рекомендуется запускать рекламные кампании и акции на мебель в более теплые месяцы. Можно также предоставлять специальные предложения для стимулирования покупок в период весенне-летнего подъема.

  • "Топ-клиенты" (Top Customer):

1.Описание: "Топ-клиенты" занимают третью по величине группу клиентов с показателем покупательной активности имеет наименьшую покупательную активность с показателем, равным 438 клиентов. Топ-клиенты имеют самый высокий средний чек в размере 1389.38, что говорит о высокой степени лояльности и активности таких клиентов. В целом, можно уделить больше внимания топ-клиентам, так как они приносят наибольший доход. Предоставить им дополнительные привилегии, скидки или бонусы, чтобы поддерживать их лояльность.

2.Предпочтительные категории: Оформление для дома занимают первое место в предпочтениях этого сегмента, что указывает на интерес клиентов к товарам, связанным с комфортом и уходом за своим жильем. Первые три позиции продуктов в сегменте "Топ-клиентов" занимают предметы для спальни, сумки и тележки, а также посуда.

3.Рекомендация: можно рассылать рекламные предложения по категориям товаров, которые проявляют сезонность от лета до осени. Это включает товары для ванны, сумки и тележки, спальни, посуду, мебель и другие подобные категории. Например, в начале лета можно предложить клиентам акции на товары для пляжа, а в осенний период - на предметы для украшения дома к праздникам. Также стоит уделить особое внимание категории инструментов, так как наблюдается пик интереса весной. В это время можно проводить специальные акции и предложения на инструменты для сада и дачи, например. Начиная с лета и до осени, клиенты проявляют больший интерес к товарам, связанным с оформлением дома, техникой, растениями и цветами. В это время можно предлагать клиентам специальные скидки, акции или бонусы при покупке на эти категории.

Материалы¶

Презентация¶

Виктория:

Презентация: https://drive.google.com/file/d/16CN0_bTMyf1Lr5Jss5yPERNTRoOGYl6q/view?usp=sharing

In [46]:
#ниже доп.инструменты
In [ ]:
df
In [ ]:
# Укажите путь и имя файла, в который вы хотите сохранить данные
save_path = 'C:/Users/79261/Downloads/tableau_project.csv'

df.to_csv(save_path, index=False)
In [ ]:
 
In [ ]: